import logging
from django.shortcuts import render
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR, \
    HTTP_403_FORBIDDEN
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet
from rest_framework.mixins import *
from fdfs_client.client import Fdfs_client, get_tracker_conf
from LMS.settings import alipay
from config.fastdfs.config import FASTDFS_SERVER_DOMAIN
from shopping.models import *
from shopping.serializers import *
from utils.customPermissions import TeacherPermission, MyBasePermission, auto_user

logger = logging.getLogger(__name__)  # 以当前文件名生成日志
tracker_path = get_tracker_conf('config/fastdfs/client.conf')  # 配置client.conf
client = Fdfs_client(tracker_path)  # 生成一个客户端（tracker配置）


# 老师分类视图集，权限：老师，接口：增删改查
class ClassificationViewSet(ModelViewSet):
    queryset = Classification.objects.filter(parent=None, is_delete=False)
    serializer_class = ParentClassificationSerializer
    permission_classes = [TeacherPermission]


# 学生分类视图集：权限：登录用户，接口：查
class ClassificationStudentViewSet(ReadOnlyModelViewSet):
    queryset = Classification.objects.filter(parent=None, is_delete=False)
    serializer_class = ParentClassificationSerializer
    permission_classes = [IsAuthenticated]

    @action(methods=['get'], detail=True)
    def commodity(self, request, pk):
        # 根据pk获取商品对象
        try:
            classification = Classification.objects.filter(is_delete=False).get(pk=pk)
        except classification.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 判断该分类是一级分类还是二级分类
        # 方法一
        if classification.parent:
            # 二级分类下的商品
            data = classification.classification2.filter(is_delete=False)
            classification_name = f'{classification.parent.name}-{classification.name}'
        else:  # 一级分类下的商品
            data = classification.classification1.filter(is_delete=False)
            classification_name = f'{classification.name}'
        return Response({'classification': classification_name, 'commodity': CommoditySerializer(data, many=True).data})
        '''
        # 方法二
        classification_attr = 'classification1'
        classification_name = classification.name
        if classification.parent:
            classification_attr = 'classification2'
            classification_name = f'{classification.parent.name}-{classification.name}'
        data = getattr(classification, classification_attr).filter(is_delete=False)  # 根据分类级别，获取商品信息
        return Response({'classification': classification_name, 'commodity': CommoditySerializer(data, many=True).data})
        '''


# 商品上架视图集，权限：老师，接口：增删改查
class CommodityViewSet(ModelViewSet):
    queryset = Commodity.objects.filter(is_delete=False)
    serializer_class = CommoditySerializer
    permission_classes = [TeacherPermission]

    @action(methods=['post'], detail=True)
    def img(self, request, pk):
        # 根据pk获取商品对象
        try:
            commodity = Commodity.objects.get(pk=pk)
            # commodity = self.get_queryset().get(pk=pk)
        except commodity.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 获取传入的信息（商品图片文件：request.FILES）
        files = request.FILES.get('file')
        # 校验
        if not files or files.content_type not in ('image/jpeg', 'image/png', 'image/gif', 'image/jpg'):
            return Response(status=HTTP_400_BAD_REQUEST)
        # 上传商品图片到FastDFS，首先拿到图片文件的后缀名
        try:  # eg：1.jpg，以'.'作为分割，[-1]是下标，拿最后一个
            image_ext_name = files.name.split('.')[-1]
        except Exception as e:
            image_ext_name = 'png'
        try:  # 因为传入头像信息是二进制，因此用upload_by_buffer
            upload_res = client.upload_by_buffer(files.read(), file_ext_name=image_ext_name)
        except Exception as e:
            logger.error(f'图片上传错误：{e}')
            return Response(status=HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            if upload_res.get('Status') != 'Upload successed.':
                return Response({'msg': '图片上传失败，请稍后重试或联系管理员'}, status=HTTP_500_INTERNAL_SERVER_ERROR)
            else:
                # 将返回的地址储存到mysql数据库（商品图片表CommodityImg.src）
                # 因为upload_res拿到的地址是二进制的，所以需要需要decode()解码
                image_url = FASTDFS_SERVER_DOMAIN + upload_res.get('Remote file_id').decode()
                CommodityImg.objects.create(src=image_url, commodity=commodity)
                # 返回响应给前端
                return Response({'data': image_url})


# 商品展示视图集，权限：登录用户，接口：查
class CommodityStudentViewSet(ReadOnlyModelViewSet):
    queryset = Commodity.objects.filter(is_delete=False, status=1)
    serializer_class = CommoditySerializer
    permission_classes = [IsAuthenticated]

    # 畅销书接口
    @action(methods=['get'], detail=False)
    def optimization(self, request):
        queryset = Commodity.objects.order_by('-comments').order_by('-c_time')
        if len(queryset) > 5:
            queryset = queryset[:5]
        return Response(CommoditySerializer(queryset, many=True).data)
        # return Response(self.get_serializer(queryset, many=True).data)


class ShoppingCartViewSet(ModelViewSet):
    # 查询集，只能看到当前用户自己的购物车
    def get_queryset(self):
        return self.request.user.shoppingcart_set.all()

    serializer_class = ShoppingCartSerializer
    permission_classes = [IsAuthenticated]

    @auto_user
    def create(self, request, *args, **kwargs):
        return ModelViewSet.create(self, request, *args, **kwargs)

    @auto_user
    def update(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)


class OrderViewSet(ReadOnlyModelViewSet, CreateModelMixin):  # 订单不支持修改
    # 查询集，只能看到当前用户自己的订单
    def get_queryset(self):
        return self.request.user.order_set.all()

    serializer_class = OrderSerializer
    permission_classes = [IsAuthenticated]


class PaymentViewSet(ViewSet):  # 不需要操作数据库模型、序列化器和操作接口，所以直接用ViewSet
    def retrieve(self, request, pk):  # 这个方法是用来解决支付流程
        """
        1.获取指定的订单信息
        2.将订单信息传给支付宝生成对应的支付页面
        3.拼接支付地址：网关 + 订单参数
        4.返回支付页面
        :param request:
        :param pk: 订单编号order_id
        :return:
        """
        # 1.获取指定的订单信息
        try:
            order = Order.objects.get(order_id=pk, user=request.user, status=1)
        except Order.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 2.将订单信息传给支付宝生成对应的支付页面
        order_string = alipay.api_alipay_trade_page_pay(
            subject=f'LMS系统{order.order_id}',  # 订单标题
            out_trade_no=order.order_id,  # 订单编号
            total_amount=str(order.total_amount),  # 订单总金额
            return_url='http://localhost:8080/home/order/success',  # 成功支付后的回调地址
        )
        # 3.拼接支付地址：网关 + 订单参数
        pay_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string
        # 4.返回支付页面
        return Response({'pay_url': pay_url})  # 字典的key：'pay_url'必须和前端一致，value：pay_url可以随便改

    def update(self, request):  # 这个方法用来返回支付成功页面
        # 获取前端回调过来的数据（在request.query_params里）
        data = request.query_params.dict()
        try:
            sign = data.pop('sign')  # 把前端回调过来的数据去掉sign
        except KeyError:
            return Response(status=HTTP_400_BAD_REQUEST)
        # 支付宝自带的校验方法，把前端回调过来的数据和去掉sign的的数据做对比，返回的结果是'True'或'False'
        res = alipay.verify(data, sign)
        if not res:  # 如果是False，data和sign数据不一致
            return Response(HTTP_403_FORBIDDEN)
        # 如果是True，data和sign数据一致，则取出订单编号和支付宝交易号，在Payment模型新增交易数据，更新Order模型的状态
        order_id = data.get('out_trade_no')
        trade_no = data.get('trade_no')
        Payment.objects.create(order_id=order_id, trade_id=trade_no)
        Order.objects.filter(order_id=order_id, status=1).update(status=2)
        return Response({'order_id': order_id, 'trade_id': trade_no})  # 返回的数据根据前端来定
